/* ----------------------------- Noble Steed Engine----------------------------*
Authors : Daniel Randle, Alex Bourne
Date Created : Mar 5 2013

File:
	nsframebufferobject.h

Description:
	This file contains the NSFrameBufferObject class which can initialize/load/bind frame
	buffer objects
*---------------------------------------------------------------------------*/

#ifndef NSFRAMEBUFFEROBJECT_H
#define NSFRAMEBUFFEROBJECT_H


#include <myGL\glew.h>
#include <global.h>
#include <nsmath3d.h>
#include <map>
#include <vector>

/* ---------------------------Class NSFrameBufferObject------------------------*

Description:
	NSFrameBufferObject will be responsible for creating and using frame buffers

*-------------------------------------------------------------------------------*/

// Forward declarations
class NSObjectSet;
class NSMapArea;
class NSCamera;
class NSShader;
class NSShadowShader;
class NSPickingShader;
class NSMesh;
class NSGBuffer;
class NSObjectReference;
class NSTexture;

class NSFrameBufferObject
{
public:
	enum BufferType {Render, Shadow, GBuffer, BufferCount};

	NSFrameBufferObject(NSObjectSet * _objects, NSMapArea * mapArea, int width = 0, int height = 0);
	~NSFrameBufferObject();

	virtual bool init(int width=0, int height=0);
	virtual void draw(const NSMatrix4Df & proj, NSCamera * camera);

	void use();
	void disable();
	virtual bool resetSize(int width, int height);
	int height();
	int width();

protected:
	int h;
	int w;
	GLuint FBO;
	NSObjectSet * objects;
	NSMapArea * mapArea;
	BufferType bType;
};

class NSGBuffer : public NSFrameBufferObject
{
public:
	struct ObjectIndex 
	{
		ObjectIndex();
		unsigned int objectIndex;
		unsigned int referenceIndex;
		unsigned int primIndex;
	};

	enum TextureType { Position, Diffuse, Normal, Pick, TexNum };
	enum TextureAttachment { Final = GL_COLOR_ATTACHMENT5, 
		ShadowDepth = GL_DEPTH_ATTACHMENT, 
		Depth = GL_DEPTH_STENCIL_ATTACHMENT};

	NSGBuffer( NSObjectSet * _objects, 
		NSMapArea * mapArea, 
		int width=0, 
		int height=0 );

	~NSGBuffer();

	void blendPointLights(const NSMatrix4Df & projCamMat, 
		const NSVec3Df & camPos, 
		const NSVec2Df & screenSize);
	void blendSpotLights(const NSMatrix4Df & projCamMat, 
		const NSVec3Df & camPos,
		const NSVec2Df & screenSize,
		const NSMatrix4Df & proj,
		NSCamera* cam);

	void draw(const NSMatrix4Df & proj, NSCamera * camera);
	void drawGeometry(const NSMatrix4Df & proj, NSCamera * camera);
	void drawDeferred(const NSMatrix4Df & proj, NSCamera * camera);
	void drawToScreenFBO();
	void drawTranslucent(const NSMatrix4Df & proj, 
		NSCamera * camera, 
		std::map<float, std::vector<std::pair<NSObjectReference*,NSShader*>>> & depthSort);

	void enableTextures();
	void enableShadowTexture();

	ObjectIndex getObjectIndex(int mouseX, int mouseY);

	bool init(int width, int height);

	bool resetSize(int width, int height);

	void useShadow();

private:
	void _lightPass(const NSMatrix4Df & projCamMat, 
		const NSVec3Df & camPos, 
		const NSVec2Df & screenSize, 
		NSObjectReference * light);
	void _stencilPass(const NSMatrix4Df & projCamMat, 
		const NSVec3Df & camPos, 
		const NSVec2Df & screenSize, 
		NSObjectReference * light);
	void _spotLightShadowPass(NSObjectReference * light);

	GLuint textures[TexNum];

	GLuint depthTexture;
	GLuint finalTexture;
	GLuint shadowTexture;
	GLuint shadowFBO;
	NSShader * shShader;
	NSShader * fsQuad;
	NSShader * fogS;
	NSMesh * quad;
	NSTexture * tex;
};

#endif